package me.yxy.mobanddd.domain.shipment;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import me.yxy.mobanddd.domain.transportorder.TransportOrder;
import me.yxy.mobanddd.domain.transportorder.TransportOrderStatus;
import me.yxy.mobanddd.domain.warehouse.Warehouse;
import org.springframework.beans.factory.annotation.Autowired;

import javax.persistence.*;
import java.util.*;

@Setter
@Getter
@Entity
@Table(name = "t_shipment")
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
public class Shipment {

    /**
     * 关于Domain对象的属性，这里有些有意思的思考：
     * 直觉上来说，我们会认为就把DTO对象中的属性直接复制过来
     * 其实，仔细想想：如果根本用不到，为什么要复制过来？
     *
     * 为什么会用不到呢？不是那么多列表，查询，都需要获取数据么？
     * - 亲爱的，那是View，不是Entity！！！无论查询报表大小与否，那都是View，都是一种Immutable的VO，可能凑巧和对应的DTO很像，但是依旧是两个完全不一样的东西！和Entity更加无关！
     * - Entity中所需要带有的，只有关键的标志信息，比如ID，Unikey等等，用来在跨领域场景中标志自己，来进行行为操作（这个非常符合DDD大神书本中的概念描述了），
     *   这也再一次证明了，Entity，侧重于行为表现
     *
     *   //--------这是之前的思考，现在再次将两者合二为一
     */
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private Date setoffTime;

    private Date arriveTime;

    private Double totalDistance;

    private Double totalFee;

    private Integer totalDuration;

    private Integer type;

    @Enumerated
    private ShipmentStatus status;

    protected Shipment() {
    }

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "shipment_id")
    @JsonIgnore //不需要再当作view的时候展示出去
    private List<TransportOrder> transportOrders;


    @Autowired
    @Transient
    ShipmentComfirmSupporter shipmentComfirmSupporter;

    /**
     * 确认调度单
     *
     * DDD: 思考：
     * <p>什么样的Command是适合放在DomainEntity里面的？如果有以下特点，则优先考虑</p>
     *
     * <ul>
     *     <li>在外部交互流程操作时，此Entity能够被明确定位到，比如“选择某个用户”，“选择某个订单”，这都是明确定位都某一条数据（哪怕是多选，那也是批量定位，属于本范畴）</li>
     *     <li>操作过程中，没有对其他领域或者其他Entity进行修改（可以查询），也就是说，事务处理围绕本Entity展开（注意，比如转账业务，两个账户对象，是两个Entity，所以不属于本范畴）</li>
     *     <li>如果复杂度较大，可以适当放在其他辅助类中，类似{@link ShipmentComfirmSupporter}，但注意辅助类要放在完全相同的目录下，因为DoaminEntity的set方法被要求必须是package可见</li>
     * </ul>
     *
     * 若不满足，则往往是相对更复杂的流程，跨领域读+写操作，跨多个Entity之间交互，那么自然而然采用 DomainService，其中可以直接用Repository任意操作
     */
    public void comfirm(){
        shipmentComfirmSupporter.process(this);
    }

    /**
     * 单独重算总里程，从{@link #comfirm()}方法中单独拆分出来的逻辑，这个方法，显然是随着业务变化而重构出来的
     * 往往在需求的一开始，不会有这个方法，自然也不会意料到“重算总里程”会需要单独隔离出来进行复用
     */
    public void recalculateMileage() {
        shipmentComfirmSupporter.calculateMileage(this);
    }
}
